package com.firexxx.common.event;

import com.firexxx.common.constants.Constants;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.Advice;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.weaver.AnnotatedElement;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;

import static java.util.stream.Collectors.joining;

/**
 * Created by mmxu6 on 2019-09-01.
 */
@Configuration
@ConditionalOnClass({EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
        AnnotatedElement.class})
@Aspect
@Slf4j
public class EventListenerLogger {
    @Around("@annotation(org.springframework.context.event.EventListener)")
    public Object before(ProceedingJoinPoint point) throws Throwable {
        Method method = ((MethodSignature) point.getSignature()).getMethod();

        int eventPrecedence = Optional.ofNullable(method.getAnnotation(Order.class)).map(Order::value).orElse(Ordered.LOWEST_PRECEDENCE);
        String event = Arrays.stream(point.getArgs()).map(arg -> arg.getClass().getSimpleName()).collect(joining(Constants.SEPARATOR_COMMA));
        String eventId = Arrays.stream(point.getArgs()).filter(arg -> arg instanceof Event).findFirst().map(o -> ((Event) o).getEventId()).orElse(Constants.STRING_EMPTY);

        log.info("{}, Handling event: {} - [{}], order: {}", eventId, point.getSignature().toShortString(), event, eventPrecedence);
        long startTime = System.currentTimeMillis();
        Object response = point.proceed();
        log.info("{}, Handled event. used time: {} ms \n", eventId, System.currentTimeMillis() - startTime);
        return response;
    }
}
